package com.yuneec.droneservice.protocol.analysis;

import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;

import com.yuneec.droneservice.ConnectionListener;
import com.yuneec.droneservice.tools.RCLog;

import java.io.Closeable;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * Created by zd.zhang on 2017/4/13.
 */

public class AOAMonitor extends UsbMonitor {
    private HandlerThread handlerThread;
    private AOAMonitorHandler monitorHandler;


    public AOAMonitor(Context context, IAnalysis analysis) {
        super(context);
        this.handlerThread = new HandlerThread("aoa monitor thread");
        this.handlerThread.start();
        this.monitorHandler = new AOAMonitorHandler(handlerThread.getLooper(), context, analysis);
    }

    public void setConnectListener(ConnectionListener listener) {
        this.monitorHandler.listener = listener;
    }


    @Override
    void onStart(Context context) {
        monitorHandler.sendEmptyMessage(AOAMonitorHandler.ACTION_SCAN);
    }

    @Override
    void onAttach() {
        RCLog.d("USB.onAttach");
        monitorHandler.removeCallbacksAndMessages(null);
        monitorHandler.sendEmptyMessageDelayed(AOAMonitorHandler.ACTION_SCAN, 200);
    }

    @Override
    void onDetach() {
        monitorHandler.removeCallbacksAndMessages(null);
        monitorHandler.sendEmptyMessage(AOAMonitorHandler.ACTION_CANCEL);
        RCLog.d("USB.onDetach");
    }

    @Override
    void onDestroy(Context context) {
        this.monitorHandler.removeCallbacksAndMessages(null);
        this.monitorHandler = null;
        this.handlerThread.quit();
        this.handlerThread = null;
    }

    private static class AOAMonitorHandler extends Handler {
        private static final int ACTION_SCAN = 0x55;
        private static final int ACTION_READ = 0x66;
        private static final int ACTION_CANCEL = 0x77;
        protected static final String ACTION_USB_PERMISSION = "com.ukey.USB_PERMISSION";

        private UsbManager mUsbManager;
        private ConnectionListener listener;
        private boolean isRequestingPermission = false;
        private PendingIntent mPermissionIntent;
        private ParcelFileDescriptor mParcelFileDescriptor;
        private InputStream mInputStream;
        private OutputStream mOutputStream;
        private IAnalysis analysis;
        private byte[] lock = new byte[0];

        private AOAMonitorHandler(Looper looper, Context context, IAnalysis analysis) {
            super(looper);
            this.mUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
            mPermissionIntent = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), 0);
            this.analysis = analysis;
        }


        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case ACTION_SCAN:
                    final UsbAccessory[] accessoryList = mUsbManager.getAccessoryList();
                    if (accessoryList == null || accessoryList.length == 0) {
                        sendEmptyMessageDelayed(ACTION_SCAN, 200);
                        return;
                    }
                    maybeAttachAccessory(accessoryList[0]);
                    break;
                case ACTION_READ:
                    if (isStartReading) {
                        try {
                            analysis.read(mInputStream);
                        } catch (IOException exception) {
                            removeMessages(AOAMonitorHandler.ACTION_SCAN);
                            sendEmptyMessage(ACTION_CANCEL);
                            return;
                        } catch (Exception e) {
                            analysis.throwException(e, true);
                        }
                        sendEmptyMessage(ACTION_READ);
                    }
                    break;
                case ACTION_CANCEL:
                    analysis.stop();
                    close();
                    synchronized (lock) {
                        if (isStartReading) {
                            if (listener != null) {
                                listener.onDisconnected();
                            }
                            isStartReading = false;
                        }
                    }
                    break;
            }
        }

        private boolean isStartReading = false;

        private void maybeAttachAccessory(final UsbAccessory accessory) {
            if (accessory != null && !isStartReading) {
                if (mUsbManager.hasPermission(accessory)) {
                    final ParcelFileDescriptor parcelFileDescriptor = mUsbManager.openAccessory(accessory);
                    if (parcelFileDescriptor != null) {
                        synchronized (lock) {
                            if (isStartReading) {
                                return;
                            }
                            isStartReading = true;
                            initAOA(parcelFileDescriptor);
                            if (listener != null) {
                                try {
                                    listener.onConnected();
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }
                            sendEmptyMessage(ACTION_READ);
                        }
                        return;
                    }
                } else {
                    if (!isRequestingPermission) {
                        //没有usb权限
                        synchronized (this) {
                            if (!isRequestingPermission) {
                                mUsbManager.requestPermission(accessory, mPermissionIntent);
                                isRequestingPermission = true;
                            }
                        }
                    }
                }
            }
            sendEmptyMessageDelayed(ACTION_SCAN, 200);
        }

        private boolean initAOA(ParcelFileDescriptor fileDescriptor) {
            try {
                if (fileDescriptor == null) {
                    return false;
                }
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                    fileDescriptor.checkError();
                }
                this.mParcelFileDescriptor = fileDescriptor;
                final FileDescriptor fd = mParcelFileDescriptor.getFileDescriptor();
                if (fd == null || !fd.valid()) {
                    return false;
                }
                FileInputStream fis = new FileInputStream(fd);
                this.mInputStream = fis;
                this.mOutputStream = new FileOutputStream(fd);
                this.analysis.start(mOutputStream);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }

        private void close() {
            if (mInputStream != null) {
                closeQuietly(mInputStream);
                mInputStream = null;
            }
            if (mOutputStream != null) {
                closeQuietly(mOutputStream);
                mOutputStream = null;
            }
            if (mParcelFileDescriptor != null) {
                try {
                    mParcelFileDescriptor.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                mParcelFileDescriptor = null;
            }
        }

        private void closeQuietly(Closeable closable) {
            try {
                closable.close();
            } catch (IOException e) {
                // pass
                e.printStackTrace();
            }
        }
    }

}
